package fr.minuskube.inv.content;

import fr.minuskube.inv.ClickableItem;
import fr.minuskube.inv.SmartInventory;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public interface InventoryContents {
  SmartInventory inventory();
  
  Pagination pagination();
  
  Optional<SlotIterator> iterator(String paramString);
  
  SlotIterator newIterator(String paramString, SlotIterator.Type paramType, int paramInt1, int paramInt2);
  
  SlotIterator newIterator(SlotIterator.Type paramType, int paramInt1, int paramInt2);
  
  SlotIterator newIterator(String paramString, SlotIterator.Type paramType, SlotPos paramSlotPos);
  
  SlotIterator newIterator(SlotIterator.Type paramType, SlotPos paramSlotPos);
  
  ClickableItem[][] all();
  
  Optional<SlotPos> firstEmpty();
  
  Optional<ClickableItem> get(int paramInt1, int paramInt2);
  
  Optional<ClickableItem> get(SlotPos paramSlotPos);
  
  InventoryContents set(int paramInt1, int paramInt2, ClickableItem paramClickableItem);
  
  InventoryContents set(SlotPos paramSlotPos, ClickableItem paramClickableItem);
  
  InventoryContents add(ClickableItem paramClickableItem);
  
  InventoryContents fill(ClickableItem paramClickableItem);
  
  InventoryContents fillRow(int paramInt, ClickableItem paramClickableItem);
  
  InventoryContents fillColumn(int paramInt, ClickableItem paramClickableItem);
  
  InventoryContents fillBorders(ClickableItem paramClickableItem);
  
  InventoryContents fillRect(int paramInt1, int paramInt2, int paramInt3, int paramInt4, ClickableItem paramClickableItem);
  
  InventoryContents fillRect(SlotPos paramSlotPos1, SlotPos paramSlotPos2, ClickableItem paramClickableItem);
  
  <T> T property(String paramString);
  
  <T> T property(String paramString, T paramT);
  
  InventoryContents setProperty(String paramString, Object paramObject);
  
  public static class Impl implements InventoryContents {
    private SmartInventory inv;
    
    private Player player;
    
    private ClickableItem[][] contents;
    
    private Pagination pagination = new Pagination.Impl();
    
    private Map<String, SlotIterator> iterators = new HashMap<>();
    
    private Map<String, Object> properties = new HashMap<>();
    
    public Impl(SmartInventory inv, Player player) {
      this.inv = inv;
      this.player = player;
      this.contents = new ClickableItem[inv.getRows()][inv.getColumns()];
    }
    
    public SmartInventory inventory() {
      return this.inv;
    }
    
    public Pagination pagination() {
      return this.pagination;
    }
    
    public Optional<SlotIterator> iterator(String id) {
      return Optional.ofNullable(this.iterators.get(id));
    }
    
    public SlotIterator newIterator(String id, SlotIterator.Type type, int startRow, int startColumn) {
      SlotIterator iterator = new SlotIterator.Impl(this, this.inv, type, startRow, startColumn);
      this.iterators.put(id, iterator);
      return iterator;
    }
    
    public SlotIterator newIterator(String id, SlotIterator.Type type, SlotPos startPos) {
      return newIterator(id, type, startPos.getRow(), startPos.getColumn());
    }
    
    public SlotIterator newIterator(SlotIterator.Type type, int startRow, int startColumn) {
      return new SlotIterator.Impl(this, this.inv, type, startRow, startColumn);
    }
    
    public SlotIterator newIterator(SlotIterator.Type type, SlotPos startPos) {
      return newIterator(type, startPos.getRow(), startPos.getColumn());
    }
    
    public ClickableItem[][] all() {
      return this.contents;
    }
    
    public Optional<SlotPos> firstEmpty() {
      for (int row = 0; row < this.contents.length; row++) {
        for (int column = 0; column < (this.contents[0]).length; column++) {
          if (!get(row, column).isPresent())
            return Optional.of(new SlotPos(row, column)); 
        } 
      } 
      return Optional.empty();
    }
    
    public Optional<ClickableItem> get(int row, int column) {
      if (row >= this.contents.length)
        return Optional.empty(); 
      if (column >= (this.contents[row]).length)
        return Optional.empty(); 
      return Optional.ofNullable(this.contents[row][column]);
    }
    
    public Optional<ClickableItem> get(SlotPos slotPos) {
      return get(slotPos.getRow(), slotPos.getColumn());
    }
    
    public InventoryContents set(int row, int column, ClickableItem item) {
      if (row >= this.contents.length)
        return this; 
      if (column >= (this.contents[row]).length)
        return this; 
      this.contents[row][column] = item;
      update(row, column, (item != null) ? item.getItem() : null);
      return this;
    }
    
    public InventoryContents set(SlotPos slotPos, ClickableItem item) {
      return set(slotPos.getRow(), slotPos.getColumn(), item);
    }
    
    public InventoryContents add(ClickableItem item) {
      for (int row = 0; row < this.contents.length; row++) {
        for (int column = 0; column < (this.contents[0]).length; column++) {
          if (this.contents[row][column] == null) {
            set(row, column, item);
            return this;
          } 
        } 
      } 
      return this;
    }
    
    public InventoryContents fill(ClickableItem item) {
      for (int row = 0; row < this.contents.length; row++) {
        for (int column = 0; column < (this.contents[row]).length; column++)
          set(row, column, item); 
      } 
      return this;
    }
    
    public InventoryContents fillRow(int row, ClickableItem item) {
      if (row >= this.contents.length)
        return this; 
      for (int column = 0; column < (this.contents[row]).length; column++)
        set(row, column, item); 
      return this;
    }
    
    public InventoryContents fillColumn(int column, ClickableItem item) {
      for (int row = 0; row < this.contents.length; row++)
        set(row, column, item); 
      return this;
    }
    
    public InventoryContents fillBorders(ClickableItem item) {
      fillRect(0, 0, this.inv.getRows() - 1, this.inv.getColumns() - 1, item);
      return this;
    }
    
    public InventoryContents fillRect(int fromRow, int fromColumn, int toRow, int toColumn, ClickableItem item) {
      for (int row = fromRow; row <= toRow; row++) {
        for (int column = fromColumn; column <= toColumn; column++) {
          if (row == fromRow || row == toRow || column == fromColumn || column == toColumn)
            set(row, column, item); 
        } 
      } 
      return this;
    }
    
    public InventoryContents fillRect(SlotPos fromPos, SlotPos toPos, ClickableItem item) {
      return fillRect(fromPos.getRow(), fromPos.getColumn(), toPos.getRow(), toPos.getColumn(), item);
    }
    
    public <T> T property(String name) {
      return (T)this.properties.get(name);
    }
    
    public <T> T property(String name, T def) {
      return this.properties.containsKey(name) ? (T)this.properties.get(name) : def;
    }
    
    public InventoryContents setProperty(String name, Object value) {
      this.properties.put(name, value);
      return this;
    }
    
    private void update(int row, int column, ItemStack item) {
      if (!this.inv.getManager().getOpenedPlayers(this.inv).contains(this.player))
        return; 
      Inventory topInventory = this.player.getOpenInventory().getTopInventory();
      topInventory.setItem(this.inv.getColumns() * row + column, item);
    }
  }
}
